/*
 * Copyright (c) 2008-2018, RF-Embedded GmbH
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef QRFEPROTOCOLHANDLER_H
#define QRFEPROTOCOLHANDLER_H

#include <QThread>
#include <QIODevice>
#include <QPair>

#include "QrfeMessageQueue.h"
#include "QrfeProtocolConstants.h"
#include "QrfeTagEvent.h"
#include "../device/QrfeFifo.h"

namespace QrfeReaderInterface
{

    /**
     * @brief The QrfeProtocolHandler class is an implementation of the RF-Embedded standard Reader-Host-Protocol
     */
    class QrfeProtocolHandler : public QThread
    {
        Q_OBJECT

    public:

        QrfeProtocolHandler(QIODevice *device, QObject* parent = 0);
        virtual ~QrfeProtocolHandler();

        int  getResponseTimeout();
        void setResponseTimeout(int responseTimeout);

        eRFE_RET_VALUE getLastReturnCode();

        bool getBlockCyclicInventoryInterrupts();
        void setBlockCyclicInventoryInterrupts(bool block);


        bool getReaderID ( ulong &readerID );
        bool getReaderType ( ulong &readerType );
        bool getHardwareRevision ( ulong &hardwareRevision );
        bool getSoftwareRevision ( ulong &softwareRevision );
        bool getBootloaderRevision ( ulong &bootloaderRevision );
        bool getCurrentSystem ( QString &currentSystem );
        bool getCurrentState ( eRFE_CURRENT_READER_STATE &currentState );
        bool getStatusRegister ( qulonglong &statusRegister );
        bool getAntennaCount ( uchar &count );

        bool getAttenuation ( ushort &maxAttenuation, ushort &attenuation );
        bool getFrequency ( uchar &mode, uchar &maxFrequencyCount, QList<uint> &frequencies );
        bool getSensitivity ( short &maxSensitivity, short &minSensitivity, short &currentSensitivity );
        bool getLbtParams ( ushort &listenTime, ushort &idleTime, ushort &maxAllocTime, short &rssiThreshold );
        bool setAttenuation ( ushort value );

        bool reboot ( );
        bool setHeartBeat ( eRFE_HEARTBEAT_TYPE type, ushort interval = 250 );
        bool setAntennaPower ( bool on );

        bool saveSettingsPermanent ( );
        bool restoreFactorySettings ( );

        bool getParam ( ushort address, QByteArray &value );

        bool getGPIOCaps ( ulong &mask, ulong &output, ulong &input );
        bool getGPIODirection ( ulong &direction );
        bool getGPIO ( ulong &mask );

        bool doSingleInventory ( QList<QrfeTagEvent> &epc );
        bool setCyclicInventory ( bool on, ulong time = 0 );
        bool readFromTag ( QByteArray tagId, uchar mem_bank, ushort address, QByteArray passwd, uchar count, QByteArray &data );
        bool writeToTag ( QByteArray tagId, uchar mem_bank, ushort address, QByteArray passwd, QByteArray data );
        bool lockTag ( QByteArray tagId, uchar mode, uchar memory, QByteArray password );
        bool killTag ( QByteArray tagId, uchar rfu, uchar recom, QByteArray password );


    private:
        bool send2Reader(uchar com1, uchar com2);
        bool send2Reader(uchar com1, uchar com2, const QByteArray& payload);
        bool waitForResponse(uchar com1, uchar com2, uint waitTime, const QString& funcName, QByteArray& response);
        bool waitForPendingMessage(uchar id, uint waitTime, const QString& funcName, QByteArray& response);

        bool send2ReaderWithoutResponse(uchar com1, uchar com2, const QString& funcName);
        bool send2ReaderWithoutResponse(uchar com1, uchar com2, const QByteArray& payload, const QString& funcName);
        bool send2ReaderWaitForResponse(uchar com1, uchar com2, uint waitTime, const QString& funcName, QByteArray& response);
        bool send2ReaderWaitForResponse(uchar com1, uchar com2, const QByteArray& payload, uint waitTime, const QString& funcName, QByteArray& response);


    signals:
        void heartBeat ();
        void cyclicInventory (const QrfeReaderInterface::QrfeTagEvent &tagInfo);
        void stateChanged (const int newState);
        void statusRegisterChanged (const qulonglong statusRegister);
        void gpioValuesChanged ( const ulong gpioValues );

    private slots:
        void heartBeatISR ( const QByteArray& payload );
        void cyclicInventoryISR ( const QByteArray& payload );
        void stateChangedISR ( const QByteArray& payload );
        void statusRegChangedISR ( const QByteArray& payload );
        void gpioValuesChangedISR ( const QByteArray& payload );
        void operationResultISR ( const QByteArray& payload );

        void _q_emit_heartBeat();
        void _q_emit_cyclicInventory(const QrfeTagEvent &tagInfo);
        void _q_emit_stateChanged(const int newState);
        void _q_emit_statusRegisterChanged(const qulonglong statusRegister);
        void _q_emit_gpioValuesChanged ( const ulong gpioValues );

    protected:
        virtual void run ();

    private slots:
        void readFromDevice();
        void sendToDevice();

    private:
        void  computeMessage ( const QByteArray & msg );
        int   messageId(uchar command1, uchar command2);
        QrfeTagEvent parseTagEvent(const QByteArray& payload, bool& ok);
        uchar calcXORCS ( const QByteArray & data );


    private:
        bool                    m_threadInitialized;

        QrfeFifo*               m_sendBuffer;

        // Private member to store received messages according to the message id.
        QrfeMessageQueue        m_messageQueue;
        // Private member to store received messages according to the message id.
        QrfeMessageQueue        m_pendingResultsQueue;
        // Instance of the device that is used to communicate with the reader.
        QIODevice*              m_device;
        // The last return code of the reader.
        eRFE_RET_VALUE          m_lastReturnCode;
        // The time out in ms to wait for the response of the reader.
        int                     m_responseTimeOut;
        int                     m_pendingResponseTimeOut;
        // Option to block interrupts from cyclic inventory.
        bool                    m_blockCyclicInventoryInterrupts;


        // State of the state machine to parse received data
        eMessageState           m_state;
        // Current single message
        QByteArray              m_singleMsg;
        // Current payload index
        int                     m_payloadIndex;
        // Current paylod length
        int                     m_payloadLength;
    };

}

#endif // QRFEPROTOCOLHANDLER_H
